﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Lexer
{
    static class PatternLiteral
    {
        //所有的匹配都需要记得只能匹配第一个单词。
        public const string OpteratorPattern = @"^[+\-\*/<>!=]=?";
        public const string NumPattern = @"^\d+";
        public const string SinglePattern = @"^\d*\.\d*";
        public const string WordPattern = "^[a-zA-Z]+[0-9a-zA-Z]*";
        public const string BlankPattern = @"^[ \t\n\r]+";
    }
    static class stringBuilderExtension
    {
        private static Match match(string s, string p)
        {
            return Regex.Match(s, p);
        }
        private static Match match(string s, string p, RegexOptions mode)
        {
            return Regex.Match(s, p, mode);
        }

        /// <summary>
        /// 尝试提取一个操作符，如果没有返回null
        /// </summary>
        /// <param name="sb"></param>
        /// <param name="opts"></param>
        /// <returns></returns>
        public static Operator TryPickOpterator(this StringBuilder sb)
        {

            var pattern = PatternLiteral.OpteratorPattern;
            var source = sb.ToString();
            Match ret = match(source, pattern);
            if (ret.Success)
            {
                sb.Remove(ret.Index, ret.Length);
                return new Operator(ret.Value);
            }
            else
            {
                return null;
            }
        }
        /// <summary>
        /// 尝试提取一个数字，如果没有返回null
        /// </summary>
        /// <param name="sb"></param>
        /// <returns></returns>
        public static Token TryPickNum(this StringBuilder sb)
        {
            var ret = match(sb.ToString(), PatternLiteral.SinglePattern);
            if (ret.Success == false)
            {
                ret = match(sb.ToString(), PatternLiteral.NumPattern);
                if (ret.Success)
                {
                    sb.Remove(ret.Index, ret.Length);
                    return new Num(Int32.Parse(ret.Value));
                }
                else
                {
                    return null;
                }
            }
            else
            { //匹配single成功的话
                {
                    sb.Remove(ret.Index, ret.Length);
                    return new Lexer.Single(System.Single.Parse(ret.Value));
                }
            }
        }
        public static Word TryPickWord(this StringBuilder sb, Dictionary<string, Word> words)
        {
            var ret = match(sb.ToString(), PatternLiteral.WordPattern);
            if (ret.Success)
            {
                sb.Remove(ret.Index, ret.Length);
                if (words.ContainsKey(ret.Value))
                    return words[ret.Value];
                else
                {
                    var w = new Word(Tag.WORD, ret.Value);
                    words.Add(w.Lexeme, w);
                    return w;
                };
            }
            else
            {
                return null;
            }
        }
        public static void RemoveSomething(this StringBuilder sb, string pattern)
        {
            var source = sb.ToString();
            var m = match(source, pattern);
            while (m.Success)
            {
                sb.Remove(m.Index, m.Length);
                m = m.NextMatch();
            }
        }
        public static void RemoveSomething(this StringBuilder sb, string pattern, RegexOptions mode)
        {
            var source = sb.ToString();
            var m = match(source, pattern, mode);
            while (m.Success)
            {
                sb.Remove(m.Index, m.Length);
                m = m.NextMatch();
            }
        }
        public static void RemoveSomething(this StringBuilder sb, string begin, string endInclude)
        {
            string p = begin + "[^$.]*" + endInclude;
            sb.RemoveSomething(p);
        }
    }
    public class MyLexer
    {
        private string source;
        private StringBuilder stringBuilder;
        private Dictionary<string, Word> words = new Dictionary<string, Word>();
        private void reserve(Word w)
        {
            words.Add(w.Lexeme, w);
        }
        public MyLexer(string s)
        {
            source = s;
            stringBuilder = new StringBuilder(s);
            reserve(new Word(Tag.TURE, "true"));
            reserve(new Word(Tag.FALSE, "false"));
        }
        public Token Scan()
        {
            removeBlank();//去除行首
            removeComment();//去除注释已经空白
            removeBlank();//再次去除空白，因为下面的匹配是不考虑第一个存在空白的可能的。
            Token token = null;
            char fst = stringBuilder[0];
            if (fst >= '0' && fst <= '9' || fst == '.')
                //知道这种效率不够高，但是很简单就是了
                token = stringBuilder.TryPickNum();
            else if (fst >= 'a' && fst <= 'z' || fst >= 'A' && fst <= 'Z')
                token = stringBuilder.TryPickWord(words);
            else
                token = stringBuilder.TryPickOpterator();
            if (token != null)
            {
                return token;
            }
            #region returnEOFToken
            token = new Token(Tag.EOF);
            return token;
            #endregion
        }
        private void removeBlank()
        {
            stringBuilder.RemoveSomething(PatternLiteral.BlankPattern);
        }
        private void removeComment()
        {
            stringBuilder.RemoveSomething("//[^\r].+\r\n");
            var begin = "/" + "\\*";
            var end = "\\" + "*/";
            stringBuilder.RemoveSomething(begin, end); ;
        }

    }
}
